package contract

import (
	"encoding/base64"
	"fmt"
	"github.com/coming-chat/wallet-SDK/core/eth"
	"github.com/ethereum/go-ethereum/common"
	"github.com/gin-gonic/gin"
	"github.com/lunny/log"
	"math/big"
	"net/http"
	"os"
	"strings"
)

const (
	DEFAULT_TOKEN = "123456"
)

var (
	contractAddr = "0x19E43D213f594218eAFeb08A97B18eBe5979CE10"
	privateKey   = "d3b328a1044093d7933cccb5e0f09206bf6e737120d7662a9b58e89c4a2b2294"
	wallet       = newWallet(privateKey, eth.NewEthChain(), Matic_Url)
	contract     *Contract
)

func init() {
	file, err := os.ReadFile("domain_abi_test.json")
	if err != nil {
		panic(err)
	}

	contract = NewContract(string(file), contractAddr, wallet)
}

func Run() {
	r := gin.Default()

	r.Use(func(c *gin.Context) {
		data := map[string]interface{}{}
		if err := c.ShouldBind(&data); err != nil {
			c.AbortWithStatusJSON(
				http.StatusInternalServerError,
				gin.H{"error": "传参异常！"})
			return
		}

		if data["token"] == nil || DEFAULT_TOKEN != data["token"].(string) {
			c.JSON(http.StatusForbidden, gin.H{
				"msg": "身份认证失败！",
			})
			return
		}

		c.Set("data", data)
	})

	r.POST("/getContractInfo", getContractInfo)
	r.POST("/fetchMints", fetchMintsApi)
	r.POST("/fetchMintsPage", fetchMintsPageApi)
	r.POST("/register", registerApi)
	r.POST("/txQuery", txQueryApi)
	r.POST("/setRecord", setRecordApi)
	r.POST("/setIpAddr", setIpAddrApi)
	r.POST("/getTokenURIApi", getTokenURIApi)
	r.POST("/getOwnerOf", getOwnerOf)
	r.POST("/getPrice", getPrice)
	r.POST("/getAllPrice", getAllPrice)
	r.POST("/setPriceAPI", setPriceAPI)

	r.Run(":8083")
}

type Data struct {
	Id     int    `json:"id"`
	Name   string `json:"name"`
	Record string `json:"record"`
	IpAddr string `json:"ipAddr"`
	Owner  string `json:"owner"`
	Index  int    `json:"index"`
}

func fetchMintsApi(c *gin.Context) {
	_, ok := c.Get("data")
	if !ok {
		log.Error("fetchMintsApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	domains, err := fetchMints()
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	//if domains != nil && len(domains) > 0 {
	//	res := []Data{}
	//
	//	for i, v := range domains {
	//		mintRecord := ""
	//		if err := contract.InvokeQuery("records", &mintRecord, v); err != nil {
	//			log.Error(err)
	//			c.JSON(http.StatusInternalServerError, gin.H{
	//				"token": "系统繁忙，请稍后再试！",
	//			})
	//			return
	//		}
	//
	//		mintIpAddr := ""
	//		if err := contract.InvokeQuery("ipaddrs", &mintIpAddr, v); err != nil {
	//			log.Error(err)
	//			c.JSON(http.StatusInternalServerError, gin.H{
	//				"token": "系统繁忙，请稍后再试！",
	//			})
	//			return
	//		}
	//
	//		owner := common.Address{}
	//		if err := contract.InvokeQuery("domains", &owner, v); err != nil {
	//			log.Error(err)
	//			c.JSON(http.StatusInternalServerError, gin.H{
	//				"token": "系统繁忙，请稍后再试！",
	//			})
	//			return
	//		}
	//
	//		res = append(res, Data{
	//			Id:     i,
	//			Name:   v,
	//			Record: mintRecord,
	//			IpAddr: mintIpAddr,
	//			Owner:  owner.Hex(),
	//		})
	//	}
	//
	//	c.JSON(http.StatusOK, res)
	//	return
	//}

	c.JSON(http.StatusOK, domains)
}

// getContractInfo 获取所有域名
func getContractInfo(c *gin.Context) {
	tld, err := getTld(contract, nil)
	if err != nil {
		log.Error("getContractInfo", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, struct {
		TldVal             string `json:"tldVal"`
		ContractAddressVal string `json:"contractAddressVal"`
	}{tld, contractAddr})
}

// fetchMintsPageApi 分页查询虚拟币
func fetchMintsPageApi(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("fetchMintsPageApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}
	data := v.(map[string]interface{})
	val := data["page"]
	if val == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	page := val.(float64)
	val = data["limit"]
	if val == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	limit := val.(float64)

	res, err := fetchMintsPage(int64(page), int64(limit))
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

// registerApi 铸域名虚拟币
func registerApi(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("registerApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	data := v.(map[string]interface{})
	val := data["domain"]
	if val == nil || len(val.(string)) < 1 {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	domain := val.(string)
	record := ""
	if data["record"] != nil {
		record = data["record"].(string)
	}
	ipAddr := ""
	if data["ipAddr"] != nil {
		ipAddr = data["ipAddr"].(string)
	}
	desc := ""
	if data["desc"] != nil {
		desc = data["desc"].(string)
	}

	hashRes, err := register(contract, domain, record, ipAddr, desc, nil)
	if err != nil {
		log.Error("register", domain, record, ipAddr, desc, err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, hashRes)
	return
}

// setRecord 修改虚拟币域名记录
func setRecordApi(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("setIpAddrApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	data := v.(map[string]interface{})
	val := data["domain"]
	if val == nil || len(val.(string)) < 1 {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	domain := val.(string)
	record := ""
	if data["record"] != nil {
		record = data["record"].(string)
	}

	txHash, err := setRecord(contract, domain, record, nil)
	if err != nil {
		log.Error("setRecord", domain, record, err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, txHash)
	return
}

// setRecord 修改虚拟币 ip 记录
func setIpAddrApi(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("setIpAddrApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	data := v.(map[string]interface{})
	val := data["domain"]
	if val == nil || len(val.(string)) < 1 {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	domain := val.(string)
	ipAddr := ""
	if data["ipAddr"] != nil {
		ipAddr = data["ipAddr"].(string)
	}

	txHash, err := setIpAddr(contract, domain, ipAddr, nil)
	if err != nil {
		log.Error("setIpAddr", domain, ipAddr, err)
		return
	}

	c.JSON(http.StatusOK, txHash)
	return
}

// registerApi 查询交易状态
func txQueryApi(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("txQueryApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	data := v.(map[string]interface{})
	val := data["txHash"]
	if val == nil || len(val.(string)) < 1 {
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "域名不能为空！",
		})
		return
	}
	txHash := val.(string)

	res, err := contract.TransactionByHash(txHash)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func getTokenURIApi(c *gin.Context) {
	tokenId, err := tokenId(c)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": err.Error(),
		})
		return
	}

	res, err := getTokenURI(contract, int64(tokenId), nil)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func getOwnerOf(c *gin.Context) {
	tokenId, err := tokenId(c)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": err.Error(),
		})
		return
	}

	res, err := ownerOf(contract, int64(tokenId), nil)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func getPrice(c *gin.Context) {
	tokenId, err := tokenId(c)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": err.Error(),
		})
		return
	}

	res, err := price(contract, int64(tokenId), nil)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func getAllPrice(c *gin.Context) {
	tokenId, err := tokenId(c)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": err.Error(),
		})
		return
	}

	res, err := allPrice(contract, int64(tokenId), nil)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func setPriceAPI(c *gin.Context) {
	v, ok := c.Get("data")
	if !ok {
		log.Error("registerApi 请求体为空！")
		c.JSON(http.StatusInternalServerError, gin.H{
			"token": "系统繁忙，请稍后再试！",
		})
		return
	}

	data := v.(map[string]interface{})
	val := data["tokenId"]
	if val == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"tokenId": "tokenId 不能为空！",
		})
		return
	}
	tokenId := val.(float64)
	var price float64 = 0
	if data["price"] != nil {
		price = data["price"].(float64)
	}

	res, err := setPrice(contract, int64(tokenId), int64(price), nil)
	if err != nil {
		log.Error(err)
		c.JSON(http.StatusInternalServerError, "系统繁忙，请稍后再试！")
		return
	}

	c.JSON(http.StatusOK, res)
	return
}

func tokenId(c *gin.Context) (float64, error) {
	v, ok := c.Get("data")
	if !ok {
		return 0, fmt.Errorf("请求体为空！")
	}

	data := v.(map[string]interface{})
	val := data["tokenId"]
	if val == nil {
		return 0, fmt.Errorf("tokenId 不能为空！")
	}
	tokenId := val.(float64)
	return tokenId, nil
}

func fetchMints() ([]string, error) {
	domains := []string{}
	if err := contract.InvokeQuery("getAllNames", &domains); err != nil {
		return nil, err
	}
	return domains, nil
}

func fetchMintsPage(page int64, limit int64) (*struct {
	Data  []Data `json:"data"`
	Total int64  `json:"total"`
}, error) {
	domains := &struct {
		Names   []string `json:"names"`
		Records []string `json:"records"`
		Addrs   []string `json:"addrs"`
		Total   *big.Int `json:"total"`
	}{}
	if err := contract.InvokeQuery("getPageNames", &domains,
		big.NewInt(page), big.NewInt(limit)); err != nil {
		return nil, err
	}

	d := []Data{}
	var total int64 = domains.Total.Int64()
	loop := len(domains.Names)
	offset := page * limit
	if offset > 0 {
		offset -= limit
	}
	for i := 0; i < loop; i++ {
		d = append(d, Data{
			Id:     i,
			Name:   domains.Names[i],
			Record: domains.Records[i],
			IpAddr: domains.Addrs[i],
			Index:  int(offset) + i,
		})
	}

	res := &struct {
		Data  []Data `json:"data"`
		Total int64  `json:"total"`
	}{d, total}
	return res, nil
}

func register(contract *Contract, domain, record, ipAddr, desc string, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{
			Value: "32",
		}
	}
	hashRes, err := contract.InvokeTx("register", opts, struct {
		Name   string
		Record string
		Addr   string
		Desc   string
		Price  *big.Int
	}{domain, record, ipAddr, desc, big.NewInt(32)})
	if err != nil {
		return "", err
	}
	return hashRes, err
}

func setRecord(contract *Contract, domain, record string, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	hashRes, err := contract.InvokeTx("setRecord", opts, domain, record)
	if err != nil {
		return "", err
	}
	return hashRes, err
}

func setIpAddr(contract *Contract, domain, ipAddr string, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	hashRes, err := contract.InvokeTx("setIpAddr", opts, domain, ipAddr)
	if err != nil {
		return "", err
	}
	return hashRes, err
}

func getTokenURI(contract *Contract, tokenId int64, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	tokenUri := ""
	if err := contract.InvokeQuery("tokenURI", &tokenUri, big.NewInt(tokenId)); err != nil {
		return "", err
	}
	decodeString, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(tokenUri, "data:application/json;base64,", ""))
	if err != nil {
		return "", err
	}
	return string(decodeString), nil
}

func ownerOf(contract *Contract, tokenId int64, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	owner := common.Address{}
	if err := contract.InvokeQuery("ownerOf", &owner, big.NewInt(tokenId)); err != nil {
		return "", err
	}

	return strings.ReplaceAll(owner.String(), "0x", ""), nil
}

func getTld(contract *Contract, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	tldVal := ""
	if err := contract.InvokeQuery("tld", &tldVal); err != nil {
		return "", err
	}

	return tldVal, nil
}

func price(contract *Contract, tokenId int64, opts *eth.CallMethodOpts) (int64, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	priceVal := big.NewInt(0)
	if err := contract.InvokeQuery("getPrice", &priceVal, big.NewInt(tokenId)); err != nil {
		return 0, err
	}

	return priceVal.Int64(), nil
}

func allPrice(contract *Contract, tokenId int64, opts *eth.CallMethodOpts) ([]int64, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	prices := []*big.Int{}
	if err := contract.InvokeQuery("getAllPrice", &prices, big.NewInt(tokenId)); err != nil {
		return nil, err
	}

	res := []int64{}
	for _, v := range prices {
		res = append(res, (v).Int64())
	}

	return res, nil
}

func setPrice(contract *Contract, tokenId, price int64, opts *eth.CallMethodOpts) (string, error) {
	if opts == nil {
		opts = &eth.CallMethodOpts{}
	}
	hashTx, err := contract.InvokeTx("setPrice", opts, big.NewInt(tokenId), big.NewInt(price))
	if err != nil {
		return "", err
	}

	return hashTx, nil
}
